package com.alinesno.cloud.common.core.service.impl;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import javax.transaction.Transactional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.Assert;

import com.alinesno.cloud.common.core.annotations.TenantMethod;
import com.alinesno.cloud.common.facade.orm.entity.BaseEntity;
import com.alinesno.cloud.common.facade.orm.repository.IBaseJpaRepository;
import com.alinesno.cloud.common.facade.pageable.DubboPageImpl;
import com.alinesno.cloud.common.facade.pageable.DubboPageRequest;
import com.alinesno.cloud.common.facade.services.IBaseService;
import com.alinesno.cloud.common.facade.wrapper.RestWrapper;

/**
 * 服务实现基类
 * 
 * @author WeiXiaoJin
 * @since 2018年11月20日 下午8:05:00
 * @param <Jpa>
 * @param <Entity>
 * @param <ID>
 */
@Transactional
public class IBaseServiceImpl<Entity extends BaseEntity, ID> implements IBaseService<Entity, ID> {

	private static final Logger log = LoggerFactory.getLogger(IBaseServiceImpl.class);

	@Autowired
	protected IBaseJpaRepository<Entity, ID> jpa;

	public List<Entity> findAll() {
		return jpa.findAll();
	}
	
	public List<Entity> findAllByFieldProp(String fieldProp) {
		return jpa.findAllByFieldProp(fieldProp);
	}

	public List<Entity> findAll(Sort sort) {
		return jpa.findAll(sort);
	}

	public List<Entity> findAllById(Iterable<ID> ids) {
		return jpa.findAllById(ids);
	}

	public <S extends Entity> List<S> saveAll(Iterable<S> entities) {
		return jpa.saveAll(entities);
	}

	public void flush() {
		jpa.flush();
	}

	public <S extends Entity> S saveAndFlush(S entity) {
		return jpa.saveAndFlush(entity);
	}

	public void deleteInBatch(Iterable<Entity> entities) {
		jpa.deleteInBatch(entities);
	}

	public void deleteAllInBatch() {
		jpa.deleteAllInBatch();
	}

	public Entity getOne(ID id) {
		return jpa.getOne(id);
	}

	public <S extends Entity> List<S> findAll(Example<S> example) {
		return jpa.findAll(example);
	}

	public <S extends Entity> List<S> findAll(Example<S> example, Sort sort) {
		return jpa.findAll(example, sort);
	}

	public Page<Entity> findAll(Pageable pageable) {
		return jpa.findAll(pageable);
	}

	public <S extends Entity> S save(S entity) {
		return jpa.save(entity);
	}

	public Optional<Entity> findById(ID id) {
		return jpa.findById(id);
	}

	public boolean existsById(ID id) {
		return jpa.existsById(id);
	}

	public long count() {
		return jpa.count();
	}

	public void deleteById(ID id) {
		jpa.deleteById(id);
	}

	public void delete(Entity entity) {
		jpa.delete(entity);
	}

	public void deleteAll(Iterable<? extends Entity> entities) {
		jpa.deleteAll(entities);
	}

	public void deleteAll() {
		jpa.deleteAll();
	}

	public <S extends Entity> Optional<S> findOne(Example<S> example) {
		return jpa.findOne(example);
	}

	public <S extends Entity> Page<S> findAll(Example<S> example, Pageable pageable) {
		return jpa.findAll(example, pageable);
	}

	public <S extends Entity> long count(Example<S> example) {
		return jpa.count(example);
	}

	public <S extends Entity> boolean exists(Example<S> example) {
		return jpa.exists(example);
	}

	public Optional<Entity> findOne(Specification<Entity> spec) {
		return jpa.findOne(spec);
	}

	public List<Entity> findAll(Specification<Entity> spec) {
		return jpa.findAll(spec);
	}

	public Page<Entity> findAll(Specification<Entity> specification, Pageable page) {
		return jpa.findAll(specification, page);
	}

	public List<Entity> findAll(Specification<Entity> spec, Sort sort) {
		return jpa.findAll(spec, sort);
	}

	public long count(Specification<Entity> spec) {
		return jpa.count(spec);
	}

	@Transactional

	public void deleteByIds(ID[] ids) {
		for (ID id : ids) {
			jpa.deleteById(id);
		}
	}

	public boolean modifyHasStatus(ID id) {
		Assert.notNull(id, "主键不能为空.");
		Entity e = jpa.getOne(id);
		log.debug("oldEntity:{} , hasStatus:{}", e, e.getHasStatus());
		log.debug("e.getHasStatus()%2:{}", e.getHasStatus() % 2);
		e.setHasStatus((e.getHasStatus() + 1) % 2);
		e = jpa.save(e);

		log.debug("newEntity:{} , hasStatus:{}", e, e.getHasStatus());
		return true;
	}

	public List<Entity> findAllByApplicationId(String applicationId) {
		return jpa.findAllByApplicationId(applicationId);
	}

	public List<Entity> findAllByTenantId(String tenantId) {
		return jpa.findAllByTenantId(tenantId);
	}

	public List<Entity> findAllByTenantIdAndApplicationId(String tenantId, String applicationId) {
		return jpa.findAllByTenantIdAndApplicationId(tenantId , applicationId);
	}

	public List<Entity> findAll(RestWrapper restWrapper) {
		return findAll(restWrapper.toSpecification());
	}

	public Page<Entity> findAllByWrapperAndPageable(RestWrapper restWrapper) {
		
		Specification<Entity> spec = restWrapper.toSpecification();
		DubboPageRequest pageable = DubboPageRequest.of(restWrapper.getPageNumber(), restWrapper.getPageSize());
		Page<Entity> page = jpa.findAll(spec, pageable);

		DubboPageImpl<Entity> dubboPage = new DubboPageImpl<Entity>(page.getContent(), pageable, page.getTotalElements());

		return dubboPage;
	}

	@Override
	public Entity findEntityById(ID id) {
		Optional<Entity> t = jpa.findById(id) ; //.get() ;
		if(t.isPresent()) {
			return t.get() ; 
		}
		return null ; 
	}

	@Override
	public void deleteByWrapper(RestWrapper restWrapper) {
		Specification<Entity> spec = restWrapper.toSpecification();
		List<Entity> list = jpa.findAll(spec);
		jpa.deleteInBatch(list);
	}

	@Override
	public List<Entity> findTop(int number , RestWrapper restWrapper) {
		Specification<Entity> spec = restWrapper.toSpecification();
		restWrapper.orderBy("addTime", false) ;
		
		List<Entity> list = jpa.findAll(spec);
		return list ;
	}

	@TenantMethod
	@Override
	public List<Entity> tenantFindList(RestWrapper restWrapper) {
		return this.findAll(restWrapper) ;
	}

	@Override
	public Entity tenantFindOne(RestWrapper restWrapper) {
		List<Entity> list = this.findAll(restWrapper) ;

		Assert.notNull(list , "查询结果为Null'");
		Assert.isTrue(list.size()<=1 , "查询结果大于1.");
		
		return list.get(0) ; 
	}

	@SuppressWarnings("unchecked")
	@Override
	public void update(Entity entity) {
		Entity source = jpa.getOne((ID)entity.getId())  ; 
		copyNonNullProperties(entity , source);
		this.save(entity) ; 
	}

	public static void copyNonNullProperties(Object src, Object target) {
	    BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
	}

	public static String[] getNullPropertyNames (Object source) {
	    final BeanWrapper src = new BeanWrapperImpl(source);
	    java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

	    Set<String> emptyNames = new HashSet<String>();
	    for(java.beans.PropertyDescriptor pd : pds) {
	        Object srcValue = src.getPropertyValue(pd.getName());
	        if (srcValue == null) emptyNames.add(pd.getName());
	    }
	    String[] result = new String[emptyNames.size()];
	    return emptyNames.toArray(result);
	}

	@Override
	public void updateById(Entity entity , ID id) {
		Entity source = jpa.getOne(id)  ; 
		copyNonNullProperties(entity , source);
		this.save(entity) ; 
	}

	@Override
	public List<Entity> findAllByHasStatus(int hasStatus) {
		RestWrapper restWrapper = new RestWrapper() ; 
		restWrapper.eq("hasStatus", hasStatus) ; 
		return this.findAll(restWrapper);
	}

//	@Override
//	public List<Entity> findAllByHasStatus(int value) {
//		// TODO Auto-generated method stub
//		return null;
//	}
	
}
